/*
This file is part of MMM.

MMM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

MMM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with MMM.  If not, see <http://www.gnu.org/licenses/>.
*
* @package    MMM
* @author     Nikolaus Vahrenkamp
* @copyright  2013 High Performance Humanoid Technologies (H2T), Karlsruhe, Germany
*
*/

#ifndef __MMM_XML_Tools_H_
#define __MMM_XML_Tools_H_

#include "MMMCore.h"
#include "MMMImportExport.h"
#include "RapidXML/RapidXMLReader.h"
#include "RapidXML/RapidXMLWriter.h"

#include <Eigen/Core>
#include <string> 
#include <vector>
#include <map>
#include <iostream>

#include <boost/lexical_cast.hpp>

// using forward declarations here, so that the rapidXML header does not have to be parsed when this file is included
namespace rapidxml
{
	template<class Ch>
	class xml_node;
}


namespace MMM
{
	/*!
		\brief Provides some basic XML IO methods.
	*/
	namespace XML
	{
        const std::string MOTION_XML_ROOT = "MMM";
        static constexpr const char* NODE_MODEL = "Model";
        static constexpr const char* NODE_MOTION = "Motion";
        static constexpr const char* NODE_MEASUREMENT = "Measurement";
        static constexpr const char* NODE_SENSOR = "Sensor";
        static constexpr const char* NODE_SENSORCONFIGURATION = "Configuration";
        static constexpr const char* NODE_SENSORDATA = "Data";
        static constexpr const char* NODE_MODELPROCESSOR = "ModelProcessorConfig";
        static constexpr const char* ATTRIBUTE_INTERPOLATED = "interpolated";
        static constexpr const char* ATTRIBUTE_TYPE = "type";
        static constexpr const char* ATTRIBUTE_NAME = "name";
        static constexpr const char* ATTRIBUTE_ID = "id";
        static constexpr const char* ATTRIBUTE_DESCRIPTION = "description";
        static constexpr const char* ATTRIBUTE_TIMESTEP = "timestep";
        static constexpr const char* ATTRIBUTE_PATH = "path";
        static constexpr const char* ATTRIBUTE_VERSION = "version";

        //! transform to string
        std::string MMM_IMPORT_EXPORT toString(float f);

        //! transform to string
        std::string MMM_IMPORT_EXPORT toString(const Eigen::VectorXf &v, const std::string &delimiter = " ");

        /*! reads a floatarray in string format
         * @throws XMLFormatException if arraySize not matches or values are no floats
         */
        Eigen::VectorXf MMM_IMPORT_EXPORT readFloatArray(std::string floatString, unsigned int arraySize);

        /*! reads a float from string
         * @throws XMLFormatException floatString is no float
         */
        float MMM_IMPORT_EXPORT readFloat(const char* floatString);

        //! Converts an XML string of a four-dimensional matrix to a Matrix4f
        Eigen::Matrix4f MMM_IMPORT_EXPORT process4x4Matrix(RapidXMLReaderNodePtr matrixNode);

        /*! Convert a Matrix4f to an XML string.
            @name Name of the matrix node. */
        void MMM_IMPORT_EXPORT appendXML(const Eigen::Matrix4f &matrix, RapidXMLWriterNodePtr node, const std::string &name);

		//! Convert string to lower case
		std::string MMM_IMPORT_EXPORT toLowerCase(const char* c);
		
		//! In place convert of a string to lower case
		void MMM_IMPORT_EXPORT toLowerCase(std::string& aString);

        /** Converts string to any type
         * Note that for bools only "1" and "0" works
         * @throws MMMException if string is not valid type */
        template<typename T>
        T MMM_IMPORT_EXPORT convertTo(const std::string &s, const std::string &errorMsg = std::string()) {
            try {
                return boost::lexical_cast<T>(s);
            } catch (boost::bad_lexical_cast) {
                throw Exception::MMMException(errorMsg.empty() ? "Could not convert string '" + s + "' to " + typeid(T).name() : errorMsg);
            }
        }

        /** Converts "true", "false", "1", "0" to bool */
        bool MMM_IMPORT_EXPORT convertToBool(const std::string &s, const std::string &errorMsg);

		//! Transform to float
		float MMM_IMPORT_EXPORT convertToFloat(const char* s);

		//! transform to float array
		Eigen::VectorXf MMM_IMPORT_EXPORT getFloatArray(const std::string &c, int size = 0);		

        float MMM_IMPORT_EXPORT getFloatByAttributeName(rapidxml::xml_node<char>* xmlNode, const std::string& attributeName, bool optional = false);
        std::string MMM_IMPORT_EXPORT getStringByAttributeName(rapidxml::xml_node<char>* xmlNode, const std::string& attributeName, bool optional = false);

		Eigen::Matrix4f MMM_IMPORT_EXPORT processTransformTag(rapidxml::xml_node<char>* transformXMLNode);

		/*!
			Searches for attribute unit(s)="mm"/"millimeter"/"cm"/"centimeter".
			Silently ignores other attributes.
			Returns scaling factor in order to retrieve meter units.
		*/
		float MMM_IMPORT_EXPORT getUnitsScalingToMeter(rapidxml::xml_node<char>* node);

		/*!
            Checks if s equals 'true', '1' or 'yes. Ignores cases.
		*/
		bool MMM_IMPORT_EXPORT isTrue(const char* s);

		/*!
			Searches for attribute unit(s)="deg"/"degree"/"rad"/"radian".
			Silently ignores other attributes.
			Returns scaling factor in order to retrieve radian units.
		*/
		float MMM_IMPORT_EXPORT getUnitsScalingToRadian(rapidxml::xml_node<char>* node);

		/*!
			Searches for attribute unit(s)="g"/"gram"/"kg"/"kilogram"/"ton".
			Silently ignores other attributes.
			Returns scaling factor in order to retrieve kg units.
		*/
		float MMM_IMPORT_EXPORT getUnitsScalingToKG(rapidxml::xml_node<char>* node);

		Eigen::Matrix3f MMM_IMPORT_EXPORT process3x3Matrix(rapidxml::xml_node<char> *matrixXMLNode);

		/**
		* This method gets an optional attribute \p attributeName from xml_node \p xmlNode and
		* returns its value as float. 
		* When no attribute \p attributeName is present the \p standardValue is returned.
		* 
		*/
        float MMM_IMPORT_EXPORT getOptionalFloatByAttributeName(rapidxml::xml_node<char>* xmlNode, const std::string& attributeName, float standardValue);
        bool MMM_IMPORT_EXPORT getOptionalBoolByAttributeName(rapidxml::xml_node<char>* xmlNode, const std::string& attributeName, bool standardValue);

        //! Convert a Matrix3f to an XML string.
		std::string MMM_IMPORT_EXPORT toXML(const Eigen::Matrix3f &m, const std::string &tabs, bool skipMatrixTag = false);

        //! Convert a Matrix4f to an XML string.
		std::string MMM_IMPORT_EXPORT toXML(const Eigen::Matrix4f &m, const std::string &tabs, bool skipMatrixTag = false);

		//! Creates a relative path and overwrites filename, if not possible the absolute path to filename remains
		void MMM_IMPORT_EXPORT makeRelativePath(const std::string &basePath, std::string &filename);
		void MMM_IMPORT_EXPORT makeAbsolutePath(const std::string &basePath, std::string &filename);

        //! returns path string of filename
        std::string MMM_IMPORT_EXPORT getPath(const std::string& filename);

		//! returns string with file name and extension of filepath.
		std::string MMM_IMPORT_EXPORT getFileName(const std::string& filepath);

        //! returns string with file name without extension of filepath
        std::string getFileNameWithoutExtension(const std::string& filepath);

        std::string getFileExtension(const std::string& filepath);

        //! returns parent folder name of data file
        std::string getFolderName(const std::string &filepath);

        //! Checks if file exists
        bool MMM_IMPORT_EXPORT  isValidFile(const std::string &filename);

		std::string MMM_IMPORT_EXPORT getAbsoluteFile(const std::string &filename);

		std::string MMM_IMPORT_EXPORT processFileNode(rapidxml::xml_node<char> *fileNode, const std::string &basePath);

		//! Stores XML content to file
		bool MMM_IMPORT_EXPORT saveXML(const std::string &filename, const std::string &content);


		//! Adds xml header to string
		void MMM_IMPORT_EXPORT addXMLHeader(std::string &xmlString);

		std::string MMM_IMPORT_EXPORT make_relative(std::string a_From, std::string a_To);

	}
}

#endif 
